﻿#include <Buffer.h>

namespace Buffer
{
	const char Buffer::kCRLF[] = "\r\n";
	const size_t Buffer::KCheapPrepend = 8;
	const size_t Buffer::KInitialSize = 1024;

	Buffer::Buffer(size_t initialSize)
		: buffer_(initialSize), readerIndex_(KCheapPrepend), writerIndex_(KCheapPrepend)
	{
		assert(buffer_.size() == initialSize);
		assert(readerIndex_ == KCheapPrepend);
		assert(writerIndex_ == KCheapPrepend);
	}

	size_t Buffer::prependableBytes()
	{
		return readerIndex_;
	}

	size_t Buffer::readableBytes()
	{
		return writerIndex_ - readerIndex_;
	}

	size_t Buffer::writableBytes()
	{
		return buffer_.size() - writerIndex_;
	}

	void Buffer::append(const char *data, int len)
	{
		ensureWritableBytes(len);
		std::copy(data, data + len, beginWrite());
		hasWritten(len);
	}

	void Buffer::retrieveInt8()
	{
		retrieve(sizeof(int8_t));
	}

	void Buffer::retrieveInt16()
	{
		retrieve(sizeof(int16_t));
	}

	void Buffer::retrieveInt32()
	{
		retrieve(sizeof(int32_t));
	}

	void Buffer::retrieveInt64()
	{
		retrieve(sizeof(int64_t));
	}

	void Buffer::appendInt8(int8_t data)
	{
		append((char *)(&data), sizeof(int8_t));
	}

	void Buffer::appendInt16(int16_t data)
	{
		append((char *)(&data), sizeof(int16_t));
	}

	void Buffer::appendInt32(int32_t data)
	{
		append((char *)(&data), sizeof(int32_t));
	}

	void Buffer::appendInt64(int64_t data)
	{
		append((char *)(&data), sizeof(int64_t));
	}

	void Buffer::retrieve(size_t len)
	{
		assert(readableBytes() >= len);
		// 如果释放掉全部数据，则读写index回到起始点
		if (readableBytes() > len)
		{
			readerIndex_ += len;
		}
		else
		{
			retrieveAll();
		}
	}

	void Buffer::retrieveAll()
	{
		readerIndex_ = KCheapPrepend;
		writerIndex_ = KCheapPrepend;
	}

	std::string Buffer::retrieveAsString(size_t len)
	{
		assert(readableBytes() >= len);
		std::string result(peek(), len);
		retrieve(len);
		return result;
	}

	std::string Buffer::retrieveAllAsString()
	{
		return retrieveAsString(readableBytes());
	}

	char *Buffer::peek()
	{
		return begin() + readerIndex_;
	}

	ssize_t Buffer::readFd(int fd, int *saveError)
	{
		/*
		note: 使用iovec、readv进行"分散读"fd中的字节。能够指定多个临时的缓冲区，
			将读取的字节写入到多个缓冲区中。
		*/
		char extrabuf[65536];
		struct iovec vec[2];
		int writable = writableBytes();
		vec[0].iov_base = begin() + writerIndex_;
		vec[0].iov_len = writable;
		vec[1].iov_base = extrabuf;
		vec[1].iov_len = sizeof extrabuf;
		ssize_t n = sockets::readv(fd, vec, 2);
		assert(n < sizeof extrabuf + writable); // 最大的读取数量
		if (n < 0)
		{
			*saveError = errno;
		}
		else if (n <= writable)
		{
			writerIndex_ += writable;
		}
		else
		{
			writerIndex_ += writable;
			append(extrabuf, n - writable);
		}
		return n;
	}

	char *Buffer::beginWrite()
	{
		return begin() + writerIndex_;
	}

	const char *Buffer::beginWrite() const
	{
		return begin() + writerIndex_;
	}

	char *Buffer::beginRead()
	{
		return begin() + readerIndex_;
	}

	void Buffer::swap(Buffer &other)
	{
		buffer_.swap(other.buffer_);
		std::swap(readerIndex_, other.readerIndex_);
		std::swap(writerIndex_, other.writerIndex_);
	}

	const char *Buffer::beginRead() const
	{
		return begin() + readerIndex_;
	}

	void Buffer::ensureWritableBytes(size_t len)
	{
		size_t canWrite = writableBytes();
		if (len > writableBytes())
		{
			makeSpace(len);
		}
		size_t canWrite2 = writableBytes();
		size_t tes = prependableBytes();
		assert(writableBytes() >= len);
	}

	void Buffer::hasWritten(int len)
	{
		assert(writableBytes() >= len);
		writerIndex_ += len;
	}

	void Buffer::makeSpace(size_t len)
	{
		size_t total = writableBytes() + prependableBytes();
		if (len + KCheapPrepend > writableBytes() + prependableBytes())
		{
			buffer_.resize(writerIndex_ + len); // 从 writerIndex_后直接拓展len个字节
		}
		else
		{
			assert(KCheapPrepend <= readerIndex_); // 如果readerIndex_<KCheapPrepend，在copy时可能会影响buffer_.size()之后的数据
			std::copy(begin() + readerIndex_, begin() + writerIndex_, begin() + KCheapPrepend);
			size_t readable = readableBytes();
			readerIndex_ = KCheapPrepend;
			writerIndex_ = readerIndex_ + readable;
			assert(writerIndex_ < buffer_.size());
		}
	}
}
